// 将 Token 转换成 AST
export function parser(tokens) {
  let token = null;
  const rawTokens = [];

  const next = (mode) => {
    token = tokens.next(mode);

    if (!token) {
      throw new TypeError("next token is undefined");
    }
    rawTokens.push(token);
    if (
      token.type === "CommentToken" ||
      token.type === "Whitespace" ||
      token.type === "Newline"
    ) {
      return next(mode);
    }
    // console.log("parser: ", token && token.type);
  };

  const ValueLiteral = () => {
    if (
      token.type === "Id" ||
      token.type === "NumbericLiteral" ||
      token.type === "String" ||
      token.type === "RegExpToken"
    ) {
      const _token = token;
      next();
      return _token;
    }

    return null;
  };

  const ValueLiteralMust = () => {
    const next = ValueLiteral();

    if (!next) {
      throw new SyntaxError(
        `Expected token type "NumbericLiteral" or "RegExpLiteral" got "${token.type}" at 
          ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
      );
    }

    return next;
  };

  //#region *
  const MulToken = () => {
    if (token.type === "MulToken") {
      const _token = token;
      next("expression");
      return _token;
    }

    return null;
  };

  const MulExpression = (left) => {
    const op = MulToken() || DivToken();
    if (!op) return left;
    const right = ValueLiteralMust();
    const node = {
      type: "BinaryExpression",
      left,
      operatorToken: op,
      right,
      // TODO: loc
      loc: {
        file: op.loc.file,
        start: left.loc.start,
        end: right.loc.end,
      },
    };

    return MulExpression(node);
  };
  //#endregion

  //#region /
  const DivToken = () => {
    if (token.type === "DivToken") {
      const _token = token;
      next("expression");
      return _token;
    }

    return null;
  };
  //#endregion

  const take = (type, mode) => {
    if (token.type === type) {
      const _token = token;
      next(mode);
      return _token;
    }

    throw new SyntaxError(
      `Expected token type "${type}" got "${token.type}" at 
        ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
    );
  };
  const maybeTake = (type, mode) => {
    if (token.type === type) {
      const _token = token;
      next(mode);
      return _token;
    }

    return null;
  };

  //#region +
  const PlusToken = () => {
    if (token.type === "PlusToken") {
      const _token = token;
      next("expression");
      return _token;
    }

    return null;
  };

  const PlusExpression = (left) => {
    const op = PlusToken();
    if (!op) return left;
    const next = ValueLiteralMust();

    const right = MulExpression(next);
    const node = {
      type: "BinaryExpression",
      left,
      operatorToken: op,
      right,
      // TODO: loc
      loc: {
        file: op.loc.file,
        start: left.loc.start,
        end: right.loc.end,
      },
    };

    return PlusExpression(node);
  };
  //#endregion

  const BinaryExpression = () => {
    const head = ValueLiteral();
    if (!head) return null;

    return PlusExpression(MulExpression(head)); // 这里可以处理 括号啥的的传递处理
  };

  const Expression = () => {
    return BinaryExpression();
  };

  const Statement = () => {
    const expr = Expression();
    if (!expr) return null;
    take("Semicolon", "expression");
    return expr;
  };

  const Statements = () => {
    const stmts = [];
    for (; ;) {
      const stmt = Statement();
      if (!stmt) break;
      stmts.push(stmt);
    }

    return stmts;
  };


  next("expression");
  const ast = Statements();

  if (token.type !== "EndOfFileToken") {
    throw new SyntaxError(
      `Expected token type "EndOfFileToken" got "${token.type}" at 
        ${token.loc.file}:${token.loc.start.line}:${token.loc.start.column}`
    );
  }

  return {
    ast,
    tokens: rawTokens,
  };
}
